Computer programs consist of instructions that tell a computer how to operate. Usually, computer programs also provide a means for people to interact with the computer. The means for interaction can be achieved in several ways. The most common means are through the use of input data and interactive commands that the computer has been programmed to accept. Although a program may be as small as a single instruction, programs today typically consist of hundreds, thousands, or even millions of instructions. The order that instructions are executed depends on many things, including any input data provided to the program, or interactive commands given to the program.
Today, a computer programmer uses several types of existing computer programs to build and test new computer programs. In particular, a programmer typically uses an editor program to write instructions and a debugger program to test the instructions.
An editor is the computerized equivalent of a typewriter page. A programmer uses a keyboard to type instructions, and the editor displays the instructions as text on the computer screen. Each line of text that is created with an editor usually represents a single instruction. To make larger programs more manageable, it is common practice for programmers to group related instructions into separate modules, such as functions or subroutines. These modules can then be referenced and used in the program with a single instruction.
As the number of instructions increase, though, so does the probability of errors occurring in the program. Programs may contain many kinds of errors, including syntax errors and logical errors.
A syntax error in a program is analogous to a misspelled word or a grammatical mistake in a book. But unlike a person, a computer cannot guess what word the programmer intended to use. For this reason syntax errors usually, but not always, must be found and corrected, using an editor, before a program will execute any of its instructions.
Logical errors, on the other hand, are latent design defects that are usually uncovered only through extensive testing and use of the program. A programmer usually tests a program by executing the program and observing the behavior and results. Logical errors generally cause the computer to execute instructions in an unexpected way, or to produce incorrect results.
Finding and correcting errors in large programs can be a tedious and time-consuming process. Programmers have developed several methods to address this problem. In the most basic method, a programmer attempts to remove errors from the program by identifying and correcting the instruction, or instructions, that cause them. The easiest way to identify the source of an error is to execute one instruction at a time and observe the order of execution and intermediate results. Ordinarily, though, a program does not display its instructions as they are executed, nor does the program stop execution after every instruction. A separate program called a debugger must be used to interactively control execution of a program and display instructions to a programmer as they are executed.
A debugger performs many functions. First, a programmer uses a debugger to mark breakpoints in instructions. Second, a programmer uses a debugger to execute instructions in a program. A debugger executes a program's instructions, as they would normally execute, until the debugger encounters an instruction marked with a breakpoint. A breakpoint causes a debugger to halt execution of instructions. Once a debugger halts execution, a programmer can examine a program's state. A program's state includes the value of any data or variables that the program uses during execution. A programmer can also interactively control subsequent program execution once a debugger has halted execution. Subsequent execution is controlled by giving commands to the debugger. For example, a step command causes the debugger to execute the next instruction and halt execution again. A resume command causes the debugger to execute all remaining instructions unless another breakpoint is encountered. When stepping through a program one instruction at a time, though, a programmer will typically encounter an instruction that references and uses a separate module. A programmer may use a debugger to step into or step over a module. If a programmer steps into a module, the debugger executes and displays the first instruction in that module and then halts execution. Once inside a module, a programmer can examine the module's instructions and control execution just the same as outside the module. On the other hand, if a programmer steps over a module, a debugger executes all instructions inside that module without displaying them. The debugger then halts execution at the first instruction following that module. Once the programmer identifies an error using this technique of stepping through a program, the programmer can then use an editor to correct the instruction, or instructions, causing the error.
Ideally, all errors would be eliminated from the program by using the method discussed above, and there would be no need for additional methods of handling errors. But those skilled in the art will appreciate that this aspiration is not usually met. Consequently, a programmer usually also includes error-checking instructions and error-handling instructions in the program itself. These error-checking and error-handling instructions are written with an editor and incorporated into a program just the same as any other instruction. Error-checking and error-handling instructions are also executed just like any other instruction. Typically, error-checking instructions are dispersed throughout the rest of the instructions, wherever the programmer thinks such instructions are appropriate. Error-checking instructions, though, are often grouped together in a convenient location within the program. A program may contain several groups of error-checking instructions.
Error-checking instructions check program behavior and intermediate results as the program is executing. If unexpected behavior or incorrect results are encountered, the error-checking instructions generate an exception. An exception is a signal to the computer to find and execute an appropriate error-handling instruction. An exception usually includes general information about the nature of the unexpected behavior or incorrect results. For example, an exception may indicate that an instruction attempted to divide a number by zero. An error-handling instruction typically interprets an exception, gives the user a warning message consistent with the exception, and terminates program execution. This second method of using error-checking and error-handling instructions is generally used in conjunction with the first method, and one method is not usually considered a substitute for the other.
While these methods are useful, there is still much room for improvement in the art. One common problem is presented when a programmer uses a debugger to control and examine the instructions in a program. A programmer may assume that an error is in a complex series of instructions and execute other instructions without examining them. Or the programmer may simply step over modules containing many other instructions. If an error happens to be in an instruction that was unexamined or in a module that was stepped over, a programmer must usually start the process all over again. Sometimes, this process must be repeated several times to find a single error. The result is lost time and productivity.
The result is the same even if a program includes error-checking instructions, and the error-checking instructions recognize an error. Because error-checking instructions are simply part of the program itself, a debugger treats them no differently than other instructions. Sometimes the only indication that an error-checking instruction has found an error is that the next instruction executed is an error-handling instruction. Again, the programmer usually has no choice but to start the process all over again.
The underlying problem with these methods is that a debugger usually does not allow a programmer to step backwards through instructions, and consequently must start over every time a programmer needs to review previously executed instructions. U.S. Pat. No. 5,428,618 (the '618 patent) entitled “Debugger Apparatus and Method Having an Event History Recording Capability” discloses the use of an execution history file to reproduce program behavior. But the method and apparatus disclosed in the '618 patent require additional files and programs to enable a programmer to step backwards through instructions while trying to identify errors in programs. Therefore, a need exists in the art for a method and apparatus for stepping backwards through instructions without the added cost and overhead of using additional files and programs. Such an apparatus would drastically reduce or eliminate the need to engage in the tedious and time-consuming repetition that is so commonly required with currently available methods.